#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
通用辅助函数

这个模块包含各种通用的辅助函数：
1. 数据格式化和转换
2. 文件处理工具
3. 验证和校验函数
4. 分页和查询辅助
5. 其他实用工具

作者: FastAPI 学习项目
创建时间: 2024
"""

import re
import uuid
import hashlib
import os
from datetime import datetime, timezone
from typing import List, Dict, Any, Optional, Union, Tuple
from pathlib import Path
from urllib.parse import urlparse
from sqlalchemy.orm import Query
from sqlalchemy import func
from loguru import logger


def generate_uuid() -> str:
    """
    生成 UUID 字符串
    
    Returns:
        str: UUID 字符串
        
    Example:
        ```python
        unique_id = generate_uuid()
        print(unique_id)  # "123e4567-e89b-12d3-a456-426614174000"
        ```
    """
    return str(uuid.uuid4())


def generate_short_id(length: int = 8) -> str:
    """
    生成短 ID
    
    Args:
        length: ID 长度
        
    Returns:
        str: 短 ID 字符串
        
    Example:
        ```python
        short_id = generate_short_id(6)
        print(short_id)  # "a1b2c3"
        ```
    """
    import string
    import random
    
    characters = string.ascii_lowercase + string.digits
    return ''.join(random.choice(characters) for _ in range(length))


def format_datetime(
    dt: datetime, 
    format_str: str = "%Y-%m-%d %H:%M:%S",
    timezone_aware: bool = True
) -> str:
    """
    格式化日期时间
    
    Args:
        dt: 日期时间对象
        format_str: 格式化字符串
        timezone_aware: 是否考虑时区
        
    Returns:
        str: 格式化后的日期时间字符串
        
    Example:
        ```python
        now = datetime.now()
        formatted = format_datetime(now, "%Y年%m月%d日 %H:%M")
        print(formatted)  # "2024年01月15日 14:30"
        ```
    """
    try:
        if timezone_aware and dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        return dt.strftime(format_str)
    except Exception as e:
        logger.error(f"日期时间格式化失败: {e}")
        return str(dt)


def parse_datetime(date_str: str, format_str: str = "%Y-%m-%d %H:%M:%S") -> Optional[datetime]:
    """
    解析日期时间字符串
    
    Args:
        date_str: 日期时间字符串
        format_str: 格式化字符串
        
    Returns:
        Optional[datetime]: 解析后的日期时间对象，失败返回 None
    """
    try:
        return datetime.strptime(date_str, format_str)
    except ValueError as e:
        logger.warning(f"日期时间解析失败: {e}")
        return None


def validate_email(email: str) -> bool:
    """
    验证邮箱地址格式
    
    Args:
        email: 邮箱地址
        
    Returns:
        bool: 邮箱格式是否有效
        
    Example:
        ```python
        is_valid = validate_email("user@example.com")
        print(is_valid)  # True
        ```
    """
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None


def validate_url(url: str) -> bool:
    """
    验证 URL 格式
    
    Args:
        url: URL 字符串
        
    Returns:
        bool: URL 格式是否有效
    """
    try:
        result = urlparse(url)
        return all([result.scheme, result.netloc])
    except Exception:
        return False


def sanitize_filename(filename: str) -> str:
    """
    清理文件名
    
    移除或替换文件名中的非法字符
    
    Args:
        filename: 原始文件名
        
    Returns:
        str: 清理后的文件名
        
    Example:
        ```python
        clean_name = sanitize_filename("my file?.txt")
        print(clean_name)  # "my_file.txt"
        ```
    """
    # 移除或替换非法字符
    illegal_chars = r'[<>:"/\\|?*]'
    filename = re.sub(illegal_chars, '_', filename)
    
    # 移除多余的空格和点
    filename = re.sub(r'\s+', '_', filename.strip())
    filename = re.sub(r'\.+', '.', filename)
    
    # 确保文件名不为空
    if not filename or filename == '.':
        filename = 'unnamed_file'
    
    return filename


def calculate_file_hash(file_path: Union[str, Path], algorithm: str = "md5") -> Optional[str]:
    """
    计算文件哈希值
    
    Args:
        file_path: 文件路径
        algorithm: 哈希算法（md5, sha1, sha256）
        
    Returns:
        Optional[str]: 文件哈希值，失败返回 None
        
    Example:
        ```python
        hash_value = calculate_file_hash("/path/to/file.txt", "sha256")
        print(hash_value)
        ```
    """
    try:
        hash_func = getattr(hashlib, algorithm.lower())()
        
        with open(file_path, 'rb') as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_func.update(chunk)
        
        return hash_func.hexdigest()
        
    except Exception as e:
        logger.error(f"计算文件哈希失败: {e}")
        return None


def get_file_size(file_path: Union[str, Path]) -> Optional[int]:
    """
    获取文件大小
    
    Args:
        file_path: 文件路径
        
    Returns:
        Optional[int]: 文件大小（字节），失败返回 None
    """
    try:
        return os.path.getsize(file_path)
    except Exception as e:
        logger.error(f"获取文件大小失败: {e}")
        return None


def format_file_size(size_bytes: int) -> str:
    """
    格式化文件大小
    
    Args:
        size_bytes: 文件大小（字节）
        
    Returns:
        str: 格式化后的文件大小
        
    Example:
        ```python
        formatted = format_file_size(1024000)
        print(formatted)  # "1000.0 KB"
        ```
    """
    if size_bytes == 0:
        return "0 B"
    
    size_names = ["B", "KB", "MB", "GB", "TB"]
    i = 0
    
    while size_bytes >= 1024 and i < len(size_names) - 1:
        size_bytes /= 1024.0
        i += 1
    
    return f"{size_bytes:.1f} {size_names[i]}"


def paginate_query(
    query: Query, 
    page: int = 1, 
    per_page: int = 20,
    max_per_page: int = 100
) -> Tuple[List[Any], Dict[str, Any]]:
    """
    分页查询
    
    Args:
        query: SQLAlchemy 查询对象
        page: 页码（从1开始）
        per_page: 每页记录数
        max_per_page: 每页最大记录数
        
    Returns:
        Tuple[List[Any], Dict[str, Any]]: (记录列表, 分页信息)
        
    Example:
        ```python
        query = db.query(User)
        items, pagination = paginate_query(query, page=2, per_page=10)
        
        print(f"总记录数: {pagination['total']}")
        print(f"总页数: {pagination['pages']}")
        ```
    """
    try:
        # 限制每页记录数
        per_page = min(per_page, max_per_page)
        
        # 确保页码有效
        page = max(1, page)
        
        # 获取总记录数
        total = query.count()
        
        # 计算总页数
        pages = (total + per_page - 1) // per_page
        
        # 确保页码不超过总页数
        page = min(page, max(1, pages))
        
        # 计算偏移量
        offset = (page - 1) * per_page
        
        # 获取当前页数据
        items = query.offset(offset).limit(per_page).all()
        
        # 分页信息
        pagination = {
            "page": page,
            "per_page": per_page,
            "total": total,
            "pages": pages,
            "has_prev": page > 1,
            "has_next": page < pages,
            "prev_page": page - 1 if page > 1 else None,
            "next_page": page + 1 if page < pages else None
        }
        
        return items, pagination
        
    except Exception as e:
        logger.error(f"分页查询失败: {e}")
        return [], {
            "page": 1,
            "per_page": per_page,
            "total": 0,
            "pages": 0,
            "has_prev": False,
            "has_next": False,
            "prev_page": None,
            "next_page": None
        }


def create_pagination_response(
    items: List[Any], 
    pagination: Dict[str, Any],
    base_url: str = ""
) -> Dict[str, Any]:
    """
    创建分页响应
    
    Args:
        items: 数据列表
        pagination: 分页信息
        base_url: 基础 URL
        
    Returns:
        Dict[str, Any]: 分页响应数据
    """
    response = {
        "items": items,
        "pagination": pagination
    }
    
    # 添加链接信息
    if base_url:
        links = {}
        
        if pagination["has_prev"]:
            links["prev"] = f"{base_url}?page={pagination['prev_page']}&per_page={pagination['per_page']}"
        
        if pagination["has_next"]:
            links["next"] = f"{base_url}?page={pagination['next_page']}&per_page={pagination['per_page']}"
        
        links["first"] = f"{base_url}?page=1&per_page={pagination['per_page']}"
        links["last"] = f"{base_url}?page={pagination['pages']}&per_page={pagination['per_page']}"
        
        response["links"] = links
    
    return response


def slugify(text: str, max_length: int = 50) -> str:
    """
    将文本转换为 URL 友好的 slug
    
    Args:
        text: 原始文本
        max_length: 最大长度
        
    Returns:
        str: slug 字符串
        
    Example:
        ```python
        slug = slugify("Hello World! 你好世界")
        print(slug)  # "hello-world"
        ```
    """
    # 转换为小写
    text = text.lower()
    
    # 移除非字母数字字符，替换为连字符
    text = re.sub(r'[^a-z0-9\s-]', '', text)
    
    # 将空格替换为连字符
    text = re.sub(r'\s+', '-', text)
    
    # 移除多余的连字符
    text = re.sub(r'-+', '-', text)
    
    # 移除首尾连字符
    text = text.strip('-')
    
    # 限制长度
    if len(text) > max_length:
        text = text[:max_length].rstrip('-')
    
    return text or 'untitled'


def truncate_text(text: str, max_length: int = 100, suffix: str = "...") -> str:
    """
    截断文本
    
    Args:
        text: 原始文本
        max_length: 最大长度
        suffix: 后缀
        
    Returns:
        str: 截断后的文本
        
    Example:
        ```python
        short_text = truncate_text("这是一段很长的文本", 10)
        print(short_text)  # "这是一段很长的..."
        ```
    """
    if len(text) <= max_length:
        return text
    
    return text[:max_length - len(suffix)] + suffix


def extract_keywords(text: str, max_keywords: int = 10) -> List[str]:
    """
    提取关键词
    
    Args:
        text: 文本内容
        max_keywords: 最大关键词数量
        
    Returns:
        List[str]: 关键词列表
    """
    # 简单的关键词提取（实际项目中可能需要更复杂的算法）
    import string
    
    # 移除标点符号
    text = text.translate(str.maketrans('', '', string.punctuation))
    
    # 分割单词
    words = text.lower().split()
    
    # 过滤停用词（简化版）
    stop_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by'}
    keywords = [word for word in words if word not in stop_words and len(word) > 2]
    
    # 统计词频
    word_count = {}
    for word in keywords:
        word_count[word] = word_count.get(word, 0) + 1
    
    # 按频率排序
    sorted_words = sorted(word_count.items(), key=lambda x: x[1], reverse=True)
    
    return [word for word, count in sorted_words[:max_keywords]]


def safe_int(value: Any, default: int = 0) -> int:
    """
    安全转换为整数
    
    Args:
        value: 要转换的值
        default: 默认值
        
    Returns:
        int: 转换后的整数
    """
    try:
        return int(value)
    except (ValueError, TypeError):
        return default


def safe_float(value: Any, default: float = 0.0) -> float:
    """
    安全转换为浮点数
    
    Args:
        value: 要转换的值
        default: 默认值
        
    Returns:
        float: 转换后的浮点数
    """
    try:
        return float(value)
    except (ValueError, TypeError):
        return default


def merge_dicts(*dicts: Dict[str, Any]) -> Dict[str, Any]:
    """
    合并多个字典
    
    Args:
        *dicts: 要合并的字典
        
    Returns:
        Dict[str, Any]: 合并后的字典
    """
    result = {}
    for d in dicts:
        if isinstance(d, dict):
            result.update(d)
    return result


def deep_get(data: Dict[str, Any], keys: str, default: Any = None) -> Any:
    """
    深度获取字典值
    
    Args:
        data: 字典数据
        keys: 键路径（用点分隔）
        default: 默认值
        
    Returns:
        Any: 获取的值
        
    Example:
        ```python
        data = {"user": {"profile": {"name": "John"}}}
        name = deep_get(data, "user.profile.name")
        print(name)  # "John"
        ```
    """
    try:
        for key in keys.split('.'):
            data = data[key]
        return data
    except (KeyError, TypeError):
        return default


# 导出所有组件
__all__ = [
    "generate_uuid",
    "generate_short_id",
    "format_datetime",
    "parse_datetime",
    "validate_email",
    "validate_url",
    "sanitize_filename",
    "calculate_file_hash",
    "get_file_size",
    "format_file_size",
    "paginate_query",
    "create_pagination_response",
    "slugify",
    "truncate_text",
    "extract_keywords",
    "safe_int",
    "safe_float",
    "merge_dicts",
    "deep_get"
]